/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.clear.beans.beancontext;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import java.util.TooManyListenersException;

import java.util.Locale;

/**
 * <p>
 * This helper class provides a utility implementation of the
 * java_my.beans.beancontext.BeanContextServices interface.
 * </p>
 * <p>
 * Since this class directly implements the BeanContextServices interface,
 * the class can, and is intended to be used either by subclassing this
 * implementation, or via delegation of an instance of this class
 * from another through the BeanContextProxy interface.
 * </p>
 *
 * @author Laurence P. G. Cable
 * @version %I%, %G%
 * @since 1.2
 */

public class      BeanContextServicesSupport extends BeanContextSupport
       implements BeanContextServices {

    /**
     * <p>
     * Construct a BeanContextServicesSupport instance
     * </p>
     *
     * @param peer	The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
     * @param lcle	The current Locale for this BeanContext.
     * @param dtime	The initial state, true if in design mode, false if runtime.
     * @param visible	The initial visibility.
     * 
     */

    public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dTime, boolean visible) {
	super(peer, lcle, dTime, visible);
    }

    /**
     * Create an instance using the specified Locale and design mode.
     *
     * @param peer	The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
     * @param lcle	The current Locale for this BeanContext.
     * @param dtime	The initial state, true if in design mode, false if runtime.
     */

    public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dtime) {
	this (peer, lcle, dtime, true);
    }

    /**
     * Create an instance using the specified locale
     *
     * @param peer	The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
     * @param lcle	The current Locale for this BeanContext.
     */

    public BeanContextServicesSupport(BeanContextServices peer, Locale lcle) {
	this (peer, lcle, false, true);
    }

    /**
     * Create an instance with a peer
     *
     * @param peer	The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
     */

    public BeanContextServicesSupport(BeanContextServices peer) {
	this (peer, null, false, true);
    }

    /**
     * Create an instance that is not a delegate of another object
     */

    public BeanContextServicesSupport() {
	this (null, null, false, true);
    }

    /**
     * called by BeanContextSupport superclass during construction and 
     * deserialization to initialize subclass transient state.
     *
     * subclasses may envelope this method, but should not override it or
     * call it directly.
     */

    public void initialize() {
	super.initialize();

	services     = new HashMap(serializable + 1);
	bcsListeners = new ArrayList(1);
    }

    /**
     * Gets the <tt>BeanContextServices</tt> associated with this
     * <tt>BeanContextServicesSupport</tt>.
     *
     * @return the instance of <tt>BeanContext</tt> 
     * this object is providing the implementation for.
     */
    public BeanContextServices getBeanContextServicesPeer() { 
	return (BeanContextServices)getBeanContextChildPeer();
    }

    /************************************************************************/

    /*
     * protected nested class containing per child information, an instance
     * of which is associated with each child in the "children" hashtable.
     * subclasses can extend this class to include their own per-child state.
     *
     * Note that this 'value' is serialized with the corresponding child 'key'
     * when the BeanContextSupport is serialized.
     */

    protected class BCSSChild extends BeanContextSupport.BCSChild  {

        private static final long serialVersionUID = -3263851306889194873L;

	/*
	 * private nested class to map serviceClass to Provider and requestors
	 * listeners.
	 */

	class BCSSCServiceClassRef {

	    // create an instance of a service ref

	    BCSSCServiceClassRef(Class sc, BeanContextServiceProvider bcsp, boolean delegated) {
		super();
	
		serviceClass     = sc;

		if (delegated)
		    delegateProvider = bcsp;
		else 
		    serviceProvider  = bcsp;
	    }

	    // add a requestor and assoc listener

	    void addRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException {
		BeanContextServiceRevokedListener cbcsrl = (BeanContextServiceRevokedListener)requestors.get(requestor);

		if (cbcsrl != null && !cbcsrl.equals(bcsrl))
		    throw new TooManyListenersException();

		requestors.put(requestor, bcsrl);
	    }

	    // remove a requestor

	    void removeRequestor(Object requestor) {
		requestors.remove(requestor);
	    }

	    // check a requestors listener

	    void verifyRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException {
		BeanContextServiceRevokedListener cbcsrl = (BeanContextServiceRevokedListener)requestors.get(requestor);

		if (cbcsrl != null && !cbcsrl.equals(bcsrl))
		    throw new TooManyListenersException();
	    }

	    void verifyAndMaybeSetProvider(BeanContextServiceProvider bcsp, boolean isDelegated) {
		BeanContextServiceProvider current;

		if (isDelegated) { // the provider is delegated
		    current = delegateProvider;

		    if (current == null || bcsp == null) {
			delegateProvider = bcsp;
			return;
		    }
		} else { // the provider is registered with this BCS
		    current = serviceProvider;

		    if (current == null || bcsp == null) {
			serviceProvider = bcsp;
			return;
		    }
		}

		if (!current.equals(bcsp))
		    throw new UnsupportedOperationException("existing service reference obtained from different BeanContextServiceProvider not supported");

	    }

	    Iterator cloneOfEntries() {
		return ((HashMap)requestors.clone()).entrySet().iterator();
	    }

	    Iterator entries() { return requestors.entrySet().iterator(); }

	    boolean isEmpty() { return requestors.isEmpty(); }

	    Class getServiceClass() { return serviceClass; }

	    BeanContextServiceProvider getServiceProvider() {
		return serviceProvider;
	    }

	    BeanContextServiceProvider getDelegateProvider() {
		return delegateProvider;
	    }

	    boolean isDelegated() { return delegateProvider != null; }

 	    void addRef(boolean delegated) { 
		if (delegated) {
		    delegateRefs++;
		} else {
		    serviceRefs++;
		}
	    }


	    void releaseRef(boolean delegated) { 
                if (delegated) {
                    if (--delegateRefs == 0) {
			delegateProvider = null;
		    }
                } else {
                    if (--serviceRefs  <= 0) {
			serviceProvider = null;
		    }
		}
            }    

	    int getRefs() { return serviceRefs + delegateRefs; }

	    int getDelegateRefs() { return delegateRefs; }

	    int getServiceRefs() { return serviceRefs; }

	    /*
	     * fields
	     */

	    Class				serviceClass;

	    BeanContextServiceProvider		serviceProvider;
	    int 				serviceRefs;

	    BeanContextServiceProvider		delegateProvider; // proxy
	    int					delegateRefs;

	    HashMap				requestors = new HashMap(1);
	}

     	/*
	 * per service reference info ...
	 */

	class BCSSCServiceRef {
	    BCSSCServiceRef(BCSSCServiceClassRef scref, boolean isDelegated) {
		serviceClassRef = scref;
		delegated       = isDelegated;
	    }

	    void addRef()  { refCnt++;        }
	    int  release() { return --refCnt; }

	    BCSSCServiceClassRef getServiceClassRef() { return serviceClassRef; }

	    boolean isDelegated() { return delegated; }

	    /*
    	     * fields
	     */

	    BCSSCServiceClassRef serviceClassRef;
	    int			 refCnt	   = 1;
	    boolean		 delegated = false;
	}

	BCSSChild(Object bcc, Object peer) { super(bcc, peer); }

        // note usage of service per requestor, per service

	synchronized void usingService(Object requestor, Object service, Class serviceClass, BeanContextServiceProvider bcsp, boolean isDelegated, BeanContextServiceRevokedListener bcsrl)  throws TooManyListenersException, UnsupportedOperationException {
	
	    // first, process mapping from serviceClass to requestor(s)

	    BCSSCServiceClassRef serviceClassRef = null;

	    if (serviceClasses == null) 
		serviceClasses = new HashMap(1);
	    else
	        serviceClassRef = (BCSSCServiceClassRef)serviceClasses.get(serviceClass);

	    if (serviceClassRef == null) { // new service being used ...
	    	serviceClassRef = new BCSSCServiceClassRef(serviceClass, bcsp, isDelegated);
		serviceClasses.put(serviceClass, serviceClassRef);

	    } else { // existing service ...
		serviceClassRef.verifyAndMaybeSetProvider(bcsp, isDelegated); // throws
		serviceClassRef.verifyRequestor(requestor, bcsrl); // throws
	    }

	    serviceClassRef.addRequestor(requestor, bcsrl);
	    serviceClassRef.addRef(isDelegated);

	    // now handle mapping from requestor to service(s)

	    BCSSCServiceRef serviceRef = null;
	    Map		    services   = null;

	    if (serviceRequestors == null) {
		serviceRequestors = new HashMap(1);
	    } else {
		services = (Map)serviceRequestors.get(requestor);
	    }

	    if (services == null) {
		services = new HashMap(1);

		serviceRequestors.put(requestor, services);
	    } else 
		serviceRef = (BCSSCServiceRef)services.get(service);

	    if (serviceRef == null) {
		serviceRef = new BCSSCServiceRef(serviceClassRef, isDelegated);

		services.put(service, serviceRef);
	    } else {
		serviceRef.addRef();
	    }
	}

	// release a service reference

	synchronized void releaseService(Object requestor, Object service) {
	    if (serviceRequestors == null) return;

	    Map services = (Map)serviceRequestors.get(requestor);
	
	    if (services == null) return; // oops its not there anymore!

	    BCSSCServiceRef serviceRef = (BCSSCServiceRef)services.get(service);

	    if (serviceRef == null) return; // oops its not there anymore!
	    
	    BCSSCServiceClassRef serviceClassRef = serviceRef.getServiceClassRef();
	    boolean		       isDelegated = serviceRef.isDelegated();
	    BeanContextServiceProvider bcsp        = isDelegated ? serviceClassRef.getDelegateProvider() : serviceClassRef.getServiceProvider();

	    bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service);

	    serviceClassRef.releaseRef(isDelegated);
	    serviceClassRef.removeRequestor(requestor);

	    if (serviceRef.release() == 0) {

		services.remove(service);

		if (services.isEmpty()) {
		    serviceRequestors.remove(requestor);
		    serviceClassRef.removeRequestor(requestor);
		}

		if (serviceRequestors.isEmpty()) {
		    serviceRequestors = null;
		}

		if (serviceClassRef.isEmpty()) {
		    serviceClasses.remove(serviceClassRef.getServiceClass());
		}

		if (serviceClasses.isEmpty())
		    serviceClasses = null;
	    }
	}

	// revoke a service

	synchronized void revokeService(Class serviceClass, boolean isDelegated, boolean revokeNow) {
	    if (serviceClasses == null) return;

	    BCSSCServiceClassRef serviceClassRef = (BCSSCServiceClassRef)serviceClasses.get(serviceClass);

	    if (serviceClassRef == null) return;

	    Iterator i = serviceClassRef.cloneOfEntries();

	    BeanContextServiceRevokedEvent bcsre       = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClass, revokeNow);
	    boolean			   noMoreRefs  = false;

	    while (i.hasNext() && serviceRequestors != null) {
		Map.Entry	    		  entry    = (Map.Entry)i.next();
		BeanContextServiceRevokedListener listener = (BeanContextServiceRevokedListener)entry.getValue();

		if (revokeNow) {
		    Object  requestor = entry.getKey();
		    Map     services  = (Map)serviceRequestors.get(requestor);

		    if (services != null) {
			Iterator i1 = services.entrySet().iterator();

			while (i1.hasNext()) {
			    Map.Entry       tmp        = (Map.Entry)i1.next();

			    BCSSCServiceRef serviceRef = (BCSSCServiceRef)tmp.getValue();
			    if (serviceRef.getServiceClassRef().equals(serviceClassRef) && isDelegated == serviceRef.isDelegated()) {	
				i1.remove();
			    }
			}

			if (noMoreRefs = services.isEmpty()) {
			    serviceRequestors.remove(requestor);
			}
		    }

		    if (noMoreRefs) serviceClassRef.removeRequestor(requestor);
		}

		listener.serviceRevoked(bcsre);
	    }

	    if (revokeNow && serviceClasses != null) {
		if (serviceClassRef.isEmpty())
		    serviceClasses.remove(serviceClass);

	        if (serviceClasses.isEmpty())
		    serviceClasses = null;
	    }

	    if (serviceRequestors != null && serviceRequestors.isEmpty())
		serviceRequestors = null;
	}

	// release all references for this child since it has been unnested.

	void cleanupReferences() {

	    if (serviceRequestors == null) return;

	    Iterator requestors = serviceRequestors.entrySet().iterator();

	    while(requestors.hasNext()) {
		Map.Entry 	     tmp       = (Map.Entry)requestors.next();
		Object 	             requestor = tmp.getKey();
		Iterator 	     services  = ((Map)tmp.getValue()).entrySet().iterator();

		requestors.remove();

		while (services.hasNext()) {
		    Map.Entry       entry   = (Map.Entry)services.next();
		    Object 	    service = entry.getKey();
		    BCSSCServiceRef sref    = (BCSSCServiceRef)entry.getValue();

		    BCSSCServiceClassRef       scref = sref.getServiceClassRef();
		
		    BeanContextServiceProvider bcsp  = sref.isDelegated() ? scref.getDelegateProvider() : scref.getServiceProvider();

		    scref.removeRequestor(requestor);
		    services.remove();

		    while (sref.release() >= 0) {
			bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service);
		    }
		}
	    }

	    serviceRequestors = null;
	    serviceClasses    = null;
	}

        void revokeAllDelegatedServicesNow() {
	    if (serviceClasses == null) return;

	    Iterator serviceClassRefs  = 
		new HashSet(serviceClasses.values()).iterator();

	    while (serviceClassRefs.hasNext()) {
	        BCSSCServiceClassRef serviceClassRef = (BCSSCServiceClassRef)serviceClassRefs.next();

	    	if (!serviceClassRef.isDelegated()) continue;

	        Iterator i = serviceClassRef.cloneOfEntries();
	    	BeanContextServiceRevokedEvent bcsre       = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClassRef.getServiceClass(), true);
	    	boolean			       noMoreRefs  = false;

	    	while (i.hasNext()) {
		    Map.Entry	    		      entry     = (Map.Entry)i.next();
		    BeanContextServiceRevokedListener listener  = (BeanContextServiceRevokedListener)entry.getValue();

		    Object  			      requestor = entry.getKey();
		    Map     			      services  = (Map)serviceRequestors.get(requestor);

		    if (services != null) {
			Iterator i1 = services.entrySet().iterator();

			while (i1.hasNext()) {
			    Map.Entry       tmp        = (Map.Entry)i1.next();

			    BCSSCServiceRef serviceRef = (BCSSCServiceRef)tmp.getValue();
			    if (serviceRef.getServiceClassRef().equals(serviceClassRef) && serviceRef.isDelegated()) {	
				i1.remove();
			    }
			}

			if (noMoreRefs = services.isEmpty()) {
			    serviceRequestors.remove(requestor);
			}
		    }

		    if (noMoreRefs) serviceClassRef.removeRequestor(requestor);

		    listener.serviceRevoked(bcsre);

		    if (serviceClassRef.isEmpty())
		        serviceClasses.remove(serviceClassRef.getServiceClass());
	    	}
	    }

	    if (serviceClasses.isEmpty()) serviceClasses = null;

	    if (serviceRequestors != null && serviceRequestors.isEmpty())
		serviceRequestors = null;
        }

	/*
 	 * fields 
	 */

	private transient HashMap	serviceClasses;	   
	private transient HashMap	serviceRequestors;
    }

    /**
     * <p>
     * Subclasses can override this method to insert their own subclass
     * of Child without having to override add() or the other Collection
     * methods that add children to the set.
     * </p>
     *
     * @param targetChild the child to create the Child on behalf of
     * @param peer        the peer if the targetChild and peer are related by BeanContextProxy
     */

    protected BCSChild createBCSChild(Object targetChild, Object peer) {
	return new BCSSChild(targetChild, peer);
    }

    /************************************************************************/

	/**
	 * subclasses may subclass this nested class to add behaviors for
	 * each BeanContextServicesProvider.
	 */

	protected static class BCSSServiceProvider implements Serializable {

	    BCSSServiceProvider(Class sc, BeanContextServiceProvider bcsp) {
		super();
	
		serviceProvider = bcsp;
	    }

	    protected BeanContextServiceProvider getServiceProvider() {
		return serviceProvider;
	    }

	    /*
   	     * fields
	     */

	    protected BeanContextServiceProvider serviceProvider;
	}

    	/**
	 * subclasses can override this method to create new subclasses of
	 * BCSSServiceProvider without having to overrride addService() in
	 * order to instantiate.
	 */

	protected BCSSServiceProvider createBCSSServiceProvider(Class sc, BeanContextServiceProvider bcsp) {
	    return new BCSSServiceProvider(sc, bcsp);
	}

    /************************************************************************/

    /**
     * add a BeanContextServicesListener
     *
     * @throw new NullPointerException
     */

    public void addBeanContextServicesListener(BeanContextServicesListener bcsl) {
	if (bcsl == null) throw new NullPointerException("bcsl");

	synchronized(bcsListeners) {
	    if (bcsListeners.contains(bcsl))
		return;
	    else
	        bcsListeners.add(bcsl);
	}
    }

    /**
     * remove a BeanContextServicesListener
     */

    public void removeBeanContextServicesListener(BeanContextServicesListener bcsl) {
	if (bcsl == null) throw new NullPointerException("bcsl");

	synchronized(bcsListeners) {
	    if (!bcsListeners.contains(bcsl))
		return;
	    else
	        bcsListeners.remove(bcsl);
	}
    }

    /**
     * add a service
     */

    public boolean addService(Class serviceClass, BeanContextServiceProvider bcsp) {
	return addService(serviceClass, bcsp, true);
    }

    /**
     * add a service
     */

    protected boolean addService(Class serviceClass, BeanContextServiceProvider bcsp, boolean fireEvent) {

	if (serviceClass == null) throw new NullPointerException("serviceClass");
	if (bcsp         == null) throw new NullPointerException("bcsp");

	synchronized(BeanContext.globalHierarchyLock) {
            if (services.containsKey(serviceClass))
    	        return false;
    	    else {
    	        services.put(serviceClass,  createBCSSServiceProvider(serviceClass, bcsp));
    
    	        if (bcsp instanceof Serializable) serializable++;
    
    	        if (!fireEvent) return true;
    
    
    	        BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass);
    
    	        fireServiceAdded(bcssae);
    
    	        synchronized(children) {
    	            Iterator i = children.keySet().iterator();
    
    	            while (i.hasNext()) {
    	                Object c = i.next();
    
    	                if (c instanceof BeanContextServices) {
    		            ((BeanContextServicesListener)c).serviceAvailable(bcssae);
    	                }
    	            }
    	        }
    
    	        return true;
    	    }
	}
    }

    /**
     * remove a service
     */

    public void revokeService(Class serviceClass, BeanContextServiceProvider bcsp, boolean revokeCurrentServicesNow) {

	if (serviceClass == null) throw new NullPointerException("serviceClass");
	if (bcsp         == null) throw new NullPointerException("bcsp");

	synchronized(BeanContext.globalHierarchyLock) {
	    if (!services.containsKey(serviceClass)) return;

	    BCSSServiceProvider bcsssp = (BCSSServiceProvider)services.get(serviceClass);
	
	    if (!bcsssp.getServiceProvider().equals(bcsp)) 
	        throw new IllegalArgumentException("service provider mismatch");

	    services.remove(serviceClass);

            if (bcsp instanceof Serializable) serializable--;

	    Iterator i = bcsChildren(); // get the BCSChild values.

	    while (i.hasNext()) {
	        ((BCSSChild)i.next()).revokeService(serviceClass, false, revokeCurrentServicesNow);
	    }

	    fireServiceRevoked(serviceClass, revokeCurrentServicesNow);
	}
    }

    /**
     * has a service, which may be delegated
     */

    public synchronized boolean hasService(Class serviceClass) {
	if (serviceClass == null) throw new NullPointerException("serviceClass");

	synchronized(BeanContext.globalHierarchyLock) {
	    if (services.containsKey(serviceClass)) return true;

	    BeanContextServices bcs = null;

	    try {
	        bcs = (BeanContextServices)getBeanContext();
	    } catch (ClassCastException cce) {
	        return false;
	    }

	    return bcs == null ? false : bcs.hasService(serviceClass);
	}
    }

    /************************************************************************/

    /*
     * a nested subclass used to represent a proxy for serviceClasses delegated
     * to an enclosing BeanContext.
     */

    protected class BCSSProxyServiceProvider implements BeanContextServiceProvider, BeanContextServiceRevokedListener {
	
        BCSSProxyServiceProvider(BeanContextServices bcs) {
	    super();
		
	    nestingCtxt = bcs;
	}

	public Object getService(BeanContextServices bcs, Object requestor, Class serviceClass, Object serviceSelector) {
	    Object service = null;

	    try {
	 	service = nestingCtxt.getService(bcs, requestor, serviceClass, serviceSelector, this);
	    } catch (TooManyListenersException tmle) {
		return null;
	    }
	
	    return service;
	}

	public void releaseService(BeanContextServices bcs, Object requestor, Object service) {
	    nestingCtxt.releaseService(bcs, requestor, service);
	}

	public Iterator getCurrentServiceSelectors(BeanContextServices bcs, Class serviceClass) {
	    return nestingCtxt.getCurrentServiceSelectors(serviceClass);
	}

	public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) {
	    Iterator i = bcsChildren(); // get the BCSChild values.

	    while (i.hasNext()) {
	        ((BCSSChild)i.next()).revokeService(bcsre.getServiceClass(), true, bcsre.isCurrentServiceInvalidNow());
	    }
	}

	/*
	 * fields
	 */

	private BeanContextServices nestingCtxt;
    }

    /************************************************************************/

    /**
     * obtain a service which may be delegated
     */

     public Object getService(BeanContextChild child, Object requestor, Class serviceClass, Object serviceSelector, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException {
	if (child        == null) throw new NullPointerException("child");
	if (serviceClass == null) throw new NullPointerException("serviceClass");
	if (requestor    == null) throw new NullPointerException("requestor");
	if (bcsrl        == null) throw new NullPointerException("bcsrl");

	Object              service = null;
	BCSSChild           bcsc;
	BeanContextServices bcssp   = getBeanContextServicesPeer();

	synchronized(BeanContext.globalHierarchyLock) {
	    synchronized(children) { bcsc = (BCSSChild)children.get(child); }

	    if (bcsc == null) throw new IllegalArgumentException("not a child of this context"); // not a child ...

	    BCSSServiceProvider bcsssp = (BCSSServiceProvider)services.get(serviceClass);

	    if (bcsssp != null) {
	        BeanContextServiceProvider bcsp = bcsssp.getServiceProvider();
	        service = bcsp.getService(bcssp, requestor, serviceClass, serviceSelector);
	        if (service != null) { // do bookkeeping ...
		    try {
	                bcsc.usingService(requestor, service, serviceClass, bcsp, false, bcsrl);
		    } catch (TooManyListenersException tmle) {
		        bcsp.releaseService(bcssp, requestor, service);
		        throw tmle;
		    } catch (UnsupportedOperationException uope) {
		        bcsp.releaseService(bcssp, requestor, service);
		        throw uope; // unchecked rt exception
		    }

		    return service;
	        }
	    }


	    if (proxy != null) {

	        // try to delegate ...

	        service = proxy.getService(bcssp, requestor, serviceClass, serviceSelector);
	
	        if (service != null) { // do bookkeeping ...
		    try {
		        bcsc.usingService(requestor, service, serviceClass, proxy, true, bcsrl);
		    } catch (TooManyListenersException tmle) {
		        proxy.releaseService(bcssp, requestor, service);
		        throw tmle;
		    } catch (UnsupportedOperationException uope) {
		        proxy.releaseService(bcssp, requestor, service);
		        throw uope; // unchecked rt exception
		    }

		    return service;
	        }
	    }
	}

       	return null;
    }

    /**
     * release a service
     */

    public void releaseService(BeanContextChild child, Object requestor, Object service) {
	if (child     == null) throw new NullPointerException("child");
	if (requestor == null) throw new NullPointerException("requestor");
	if (service   == null) throw new NullPointerException("service");

	BCSSChild bcsc;

	synchronized(BeanContext.globalHierarchyLock) {
	        synchronized(children) { bcsc = (BCSSChild)children.get(child); }

	        if (bcsc != null)
		    bcsc.releaseService(requestor, service);
		else
		   throw new IllegalArgumentException("child actual is not a child of this BeanContext");
        }
    }

    /**
     * @return an iterator for all the currently registered service classes.
     */

    public Iterator getCurrentServiceClasses() {
	return new BCSIterator(services.keySet().iterator());
    }

    /**
     * @return an iterator for all the currently available service selectors
     * (if any) available for the specified service.
     */

    public Iterator getCurrentServiceSelectors(Class serviceClass) {
	
	BCSSServiceProvider bcsssp = (BCSSServiceProvider)services.get(serviceClass);
	
	return bcsssp != null ? new BCSIterator(bcsssp.getServiceProvider().getCurrentServiceSelectors(getBeanContextServicesPeer(), serviceClass)) : null;
    }

    /**
     * BeanContextServicesListener callback, propagates event to all 
     * currently registered listeners and BeanContextServices children,
     * if this BeanContextService does not already implement this service
     * itself.
     *
     * subclasses may override or envelope this method to implement their
     * own propagation semantics.
     */

     public void serviceAvailable(BeanContextServiceAvailableEvent bcssae) {
	synchronized(BeanContext.globalHierarchyLock) {
	    if (services.containsKey(bcssae.getServiceClass())) return;

	    fireServiceAdded(bcssae);

	    Iterator i;

	    synchronized(children) {
	        i = children.keySet().iterator();
	    }

	    while (i.hasNext()) {
	        Object c = i.next();

	        if (c instanceof BeanContextServices) {
	            ((BeanContextServicesListener)c).serviceAvailable(bcssae);
	        }
	    }
	}
     }

    /**
     * BeanContextServicesListener callback, propagates event to all 
     * currently registered listeners and BeanContextServices children,
     * if this BeanContextService does not already implement this service
     * itself.
     *
     * subclasses may override or envelope this method to implement their
     * own propagation semantics.
     */

    public void serviceRevoked(BeanContextServiceRevokedEvent bcssre) {
	synchronized(BeanContext.globalHierarchyLock) {
	    if (services.containsKey(bcssre.getServiceClass())) return;

	    fireServiceRevoked(bcssre);

	    Iterator i;

	    synchronized(children) {
	        i = children.keySet().iterator();
	    }

	    while (i.hasNext()) {
	        Object c = i.next();

	        if (c instanceof BeanContextServices) {
	            ((BeanContextServicesListener)c).serviceRevoked(bcssre);
	        }
	    }
	}
    }

    /**
     * Gets the <tt>BeanContextServicesListener</tt> (if any) of the specified
     * child.
     *
     * @param child the specified child
     * @return the BeanContextServicesListener (if any) of the specified child
     */
    protected static final BeanContextServicesListener getChildBeanContextServicesListener(Object child) {
	try {
	    return (BeanContextServicesListener)child;
	} catch (ClassCastException cce) {
	    return null;
	}
    }

    /**
     * called from superclass child removal operations after a child
     * has been successfully removed. called with child synchronized.
     *
     * This subclass uses this hook to immediately revoke any services
     * being used by this child if it is a BeanContextChild.
     *
     * subclasses may envelope this method in order to implement their
     * own child removal side-effects.
     */

    protected void childJustRemovedHook(Object child, BCSChild bcsc) {
	BCSSChild bcssc = (BCSSChild)bcsc;

	bcssc.cleanupReferences();
    }

    /**
     * called from setBeanContext to notify a BeanContextChild
     * to release resources obtained from the nesting BeanContext.
     * 
     * This method revokes any services obtained from its parent.
     * 
     * subclasses may envelope this method to implement their own semantics.
     */

    protected synchronized void releaseBeanContextResources() {
	Object[] bcssc;

	super.releaseBeanContextResources();

	synchronized(children) {
	    if (children.isEmpty()) return;

	    bcssc = children.values().toArray();
	}


	for (int i = 0; i < bcssc.length; i++) {
	    ((BCSSChild)bcssc[i]).revokeAllDelegatedServicesNow();
	}

	proxy = null;
    }

    /**
     * called from setBeanContext to notify a BeanContextChild
     * to allocate resources obtained from the nesting BeanContext.
     * 
     * subclasses may envelope this method to implement their own semantics.
     */

    protected synchronized void initializeBeanContextResources() {
	super.initializeBeanContextResources();

	BeanContext nbc = getBeanContext();

	if (nbc == null) return;

	try {
	    BeanContextServices bcs = (BeanContextServices)nbc;

	    proxy = new BCSSProxyServiceProvider(bcs);
	} catch (ClassCastException cce) {
	    // do nothing ...
	}
    }

    /**
     * Fires a <tt>BeanContextServiceEvent</tt> notifying of a new service.
     */
    protected final void fireServiceAdded(Class serviceClass) {
	BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass); 

	fireServiceAdded(bcssae);
    }

    /**
     * Fires a <tt>BeanContextServiceAvailableEvent</tt> indicating that a new
     * service has become available.
     *
     * @param bcssae the <tt>BeanContextServiceAvailableEvent</tt>
     */
    protected final void fireServiceAdded(BeanContextServiceAvailableEvent bcssae) {
	Object[]		         copy;

	synchronized (bcsListeners) { copy = bcsListeners.toArray(); }

	for (int i = 0; i < copy.length; i++) {
	    ((BeanContextServicesListener)copy[i]).serviceAvailable(bcssae);
	}
    }

    /**
     * Fires a <tt>BeanContextServiceEvent</tt> notifying of a service being revoked.
     *
     * @param bcsre the <tt>BeanContextServiceRevokedEvent</tt>
     */
    protected final void fireServiceRevoked(BeanContextServiceRevokedEvent bcsre) {
	Object[]		         copy;

	synchronized (bcsListeners) { copy = bcsListeners.toArray(); }

	for (int i = 0; i < copy.length; i++) {
	    ((BeanContextServiceRevokedListener)copy[i]).serviceRevoked(bcsre);
	}
    }

    /**
     * Fires a <tt>BeanContextServiceRevokedEvent</tt> 
     * indicating that a particular service is 
     * no longer available.
     */
    protected final void fireServiceRevoked(Class serviceClass, boolean revokeNow) {
	Object[]		       copy;
	BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(getBeanContextServicesPeer(), serviceClass, revokeNow); 

	synchronized (bcsListeners) { copy = bcsListeners.toArray(); }

	for (int i = 0; i < copy.length; i++) {
	    ((BeanContextServicesListener)copy[i]).serviceRevoked(bcsre);
	}
   }

    /**
     * called from BeanContextSupport writeObject before it serializes the
     * children ...
     *
     * This class will serialize any Serializable BeanContextServiceProviders
     * herein.
     *
     * subclasses may envelope this method to insert their own serialization
     * processing that has to occur prior to serialization of the children
     */

    protected synchronized void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {

	oos.writeInt(serializable);

	if (serializable <= 0) return;

	int count = 0;

	Iterator i = services.entrySet().iterator();

	while (i.hasNext() && count < serializable) {
	    Map.Entry 		entry = (Map.Entry)i.next();
	    BCSSServiceProvider bcsp  = null;

	     try {
		bcsp = (BCSSServiceProvider)entry.getValue();
	     } catch (ClassCastException cce) {
		continue;
	     }

	     if (bcsp.getServiceProvider() instanceof Serializable) {
		oos.writeObject(entry.getKey());
		oos.writeObject(bcsp);
		count++;
	     }
	}

	if (count != serializable) 
	    throw new IOException("wrote different number of service providers than expected");
    }

    /**
     * called from BeanContextSupport readObject before it deserializes the
     * children ...
     *
     * This class will deserialize any Serializable BeanContextServiceProviders
     * serialized earlier thus making them available to the children when they
     * deserialized.
     *
     * subclasses may envelope this method to insert their own serialization
     * processing that has to occur prior to serialization of the children
     */

    protected synchronized void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {

	serializable = ois.readInt();

	int count = serializable;

	while (count > 0) {
	    services.put(ois.readObject(), ois.readObject());
	    count--;
	} 
    }
 
    /**
     * serialize the instance
     */

    private synchronized void writeObject(ObjectOutputStream oos) throws IOException {
	oos.defaultWriteObject();

	serialize(oos, (Collection)bcsListeners);
    }

    /**
     * deserialize the instance
     */

    private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {

	ois.defaultReadObject();

	deserialize(ois, (Collection)bcsListeners);
    }

    
    /*
     * fields
     */

    /**
     * all accesses to the <code> protected transient HashMap services </code>
     * field should be synchronized on that object
     */
    protected transient HashMap  		 services;

    /**
     * The number of instances of a serializable <tt>BeanContextServceProvider</tt>.
     */
    protected transient int 	 		 serializable = 0;


    /**
     * Delegate for the <tt>BeanContextServiceProvider</tt>.
     */
    protected transient BCSSProxyServiceProvider proxy;


    /**
     * List of <tt>BeanContextServicesListener</tt> objects.
     */
    protected transient ArrayList		 bcsListeners;
}
